#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <stdio.h>
#include <string.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>

#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <NetworkProtocol.h>
#include <Log.h>


using boost::asio::ip::tcp;

namespace CSNetworkLib {

	/**
	 * Klasa clienta TCP, jako parametr template należy użyć klasy która dziedziczy po klasie NetworkProtocol.
	 */
	template<class PROTOCOL>
	class TCPClient
	{
		private:
			boost::asio::io_service *io_service;
			socket_ptr socket;
			PROTOCOL *m_protocol;
		public:
			/**
			 * Konstruktor podstawowy, w przypadku problemow z połączeniem rzuca wyjątek std::exception.
			 * @param ip adres ip jako string lub nazwa host.
			 * @param port port jako string, w formacje rozumianym przez klasę tcp::resolver::query.
			 */
			TCPClient(const char* ip, const char* port) : io_service(new boost::asio::io_service()), socket(new tcp::socket(*io_service))
			{
				FUNCTION_TRACK
				try {
					DBUG("Connecting: %s %s", ip, port);

					tcp::resolver resolver(*io_service);
					tcp::resolver::query query(tcp::v4(), ip, port);
					tcp::resolver::iterator iterator = resolver.resolve(query);
					
					socket->connect(*iterator);
					boost::asio::ip::tcp::no_delay option(true);
					socket->set_option(option);
					boost::asio::socket_base::keep_alive option2(true);
					socket->set_option(option2);
				} catch (std::exception& e) {
					DBUG("Exception: %s", e.what());
					throw e;
				}
				m_protocol = new PROTOCOL(socket);
				m_protocol->SetRefBack((CSNetworkLib::NetworkProtocol**)&m_protocol);
			}
			
			/**
			 * Destruktor domyślny, automatycznie przerywa połączenie z serwerem.
			 */
			~TCPClient()
			{
				FUNCTION_TRACK
				m_protocol->SetRefBack(NULL);
				m_protocol->Stop();
			}
			
			/**
			 * Metoda zwraca protokół użyty do obsługi połączenia.
			 * @return obiekt protokołu dla tego połączenia.
			 */
			inline PROTOCOL& GetProtocol()
			{
				return *m_protocol;
			}
			
			/**
			 * Metoda sprawdza czy połączenie jest aktywne.
			 * @return true jeśli socket jest aktywny.
			 */
			bool IsActive()
			{
				return m_protocol!=NULL && socket->is_open();
			}
	};
};

#endif /*TCPCLIENT_H*/
